package com.youlu.campus.service.ranking.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.result.UpdateResult;
import com.youlu.campus.base.community.RankingList;
import com.youlu.campus.base.exception.BusinessException;

import com.youlu.campus.entity.*;
import com.youlu.campus.entity.VO.RankingListQueryVO;
import com.youlu.campus.entity.VO.req.RankingListResVO;
import com.youlu.campus.entity.VO.res.ActivityRankingPointsVO;
import com.youlu.campus.service.activity.ActivityPointConfigService;
import com.youlu.campus.service.activity.ActivityService;
import com.youlu.campus.service.base.LockService;
import com.youlu.campus.service.rank.RankBaseService;
import com.youlu.campus.service.ranking.UserActivityPointRankingService;
import com.youlu.campus.service.ranking.entity.UserActivityPointRanking;
import com.youlu.campus.service.ranking.entity.UserActivityPointRankingQueryVo;
import com.youlu.campus.service.ranking.entity.UserActivityTaskRanking;
import com.youlu.campus.service.visitor.VisitorUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.youlu.campus.base.base.Constants.defaultHead;

@Slf4j
@Service
public class UserActivityPointRankingImpl implements UserActivityPointRankingService {
    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private VisitorUserService visitorUserService;
    @Autowired
    private LockService lockService;
    @Autowired
    private ActivityPointConfigService activityPointConfigService;
    @Autowired
    private RankBaseService rankBaseService;
    @Lazy
    @Autowired
    private ActivityService activityService;
    /**
     * 全国榜缓存Key
     */
    public String RANK_PRE_KEY = "com.luyou.rank.h5-plus.activityId:%s";

    @Override
    public String getRankPreKey() {
        return RANK_PRE_KEY;
    }

    @Override
    public String getRankKey(String activityId) {
        return String.format(RANK_PRE_KEY, activityId);
    }

    @Override
    public RankingListResVO getUserTaskRanking(@NotNull RankingListQueryVO req, UserPO userPO) {
        log.info(":>>> 获取用户活动积分排行榜:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getActivityId())) {
            log.error(":>>> 获取活动积分排行榜参数错误=");
            throw new BusinessException("获取活动积分排行榜参数错误");
        }
        req.setUserPO(userPO);
        return getUserPointRankList(req, userPO);
    }

    @Override
    public Page<UserActivityPointRanking> getUserActivityRanking(RankingListQueryVO req) {
        if (StringUtils.isBlank(req.getActivityId())) {
            log.error(":>>> 获取活动积分排行榜参数错误=");
            throw new BusinessException("获取活动积分排行榜参数错误");
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("activityId").is(req.getActivityId()));
        long count = mongoTemplate.count(query, UserActivityPointRanking.class);
        PageRequest pageRequest = PageRequest.of(req.getPage() - 1, req.getPageSize());
//        query.with(new Sort(Sort.Direction.DESC, "quantity"));
        Sort sort=Sort.by(Sort.Direction.DESC, "quantity");
        query.with(sort);
        List<UserActivityPointRanking> rankings = mongoTemplate.find(query.with(pageRequest), UserActivityPointRanking.class);
        return PageableExecutionUtils.getPage(rankings, pageRequest, () -> count);
    }

    @Async
    @Override
    public void updatePoint(UserPO userPO, String activityId, Integer point) {
        log.info(":>>> 开始更新用户:{} 积分全国排行榜 activityId:{},{}", userPO == null ? null : userPO.getId(), activityId, point);
        String key = String.format("com.luyou.updatePoint:%s:activityId:%s", userPO.getId(), activityId);
        try {
            boolean lock = lockService.tryLock(key, 60, TimeUnit.SECONDS);
            log.info(":>>> 获取用户:{} 活动:{} 积分排行榜锁结果:{}", userPO.getId(), activityId, lock);
            if (!lock) {
                lockService.unLock(key);
                return;
            }
            UserActivityPointRanking db = this.find(userPO.getId(), userPO.getProvince(), activityId);
            String countryRankKey = this.getRankKey(activityId);
            if (Objects.isNull(db)) {
                log.info(":>>> 用户:{} 活动:{} 积分排行榜不存在新建", userPO.getId(), activityId);
                db = new UserActivityPointRanking();
                db.setActivityId(activityId);
                db.setUserId(userPO.getId());
                db.setImageUrl(userPO.getAvatarUrl());
                db.setName(userPO.getNickName());
                db.setQuantity(Double.valueOf(point + ""));
                db.setActivityId(activityId);
                db.setCreatedTime(new Date());
                db.setUniversityId(userPO.getUniversityId());
                mongoTemplate.insert(db);
                rankBaseService.addSingleRank(countryRankKey, userPO.getId(), Double.valueOf(point + ""));
                return;
            }
            Query query = new Query().addCriteria(Criteria.where("id").is(db.getId()));
            Update update = new Update();
            update.inc("quantity", Double.valueOf(point + ""));
            update.set("updatedTime", new Date());
            UpdateResult updateResult = mongoTemplate.updateFirst(query, update, UserActivityPointRanking.class);
            rankBaseService.remove(countryRankKey, userPO.getId());
            rankBaseService.addSingleRank(countryRankKey, userPO.getId(), Double.valueOf(point + "") + db.getQuantity());
            log.info(":>>> 用户活动积分排行榜存在更新:{},结果:{}", userPO.getId(), updateResult.getModifiedCount());
        } catch (Exception e) {
            log.error(":>>> 更新用户活动积分排行榜错误:{}", e);
        } finally {
            lockService.unLock(key);
        }
    }

    @Override
    public UserActivityPointRanking find(String userId, String province, String activityId) {
        Query query = new Query().addCriteria(Criteria.where("userId").is(userId));
        if (StringUtils.isNotBlank(activityId)) {
            query.addCriteria(Criteria.where("activityId").is(activityId));
        }
        return mongoTemplate.findOne(query, UserActivityPointRanking.class);
    }

    @Override
    public UserActivityPointRanking findByUserIdAndActivityId(String userId, String activityId) {
        Query query = new Query().addCriteria(Criteria.where("userId").is(userId));
        query.addCriteria(Criteria.where("activityId").is(activityId));
        return this.mongoTemplate.findOne(query, UserActivityPointRanking.class);
    }

    @Override
    public boolean hasPoint(String userId, String activityId, String bizType) {
        UserActivityPointRanking u = find(userId, activityId);
        if (Objects.isNull(u) || Objects.isNull(u.getQuantity())) {
            log.info(":>>> 用户:{},活动:{},贡献值为空", userId, activityId);
            return false;
        }
        ActivityPointConfig activityPointConfig = activityPointConfigService.findByActivityId(activityId);
        if (Objects.isNull(activityPointConfig) || Objects.isNull(activityPointConfig.getRules().get(bizType))) {
            log.info(":>>> 活动没有配置贡献值规则:{},{}", activityId, bizType);
            return false;
        }
        log.info(":>>> 用户活动:{},{},贡献值:{},业务:{},需要的贡献值为:{}", userId, activityId, u.getQuantity(), bizType, activityPointConfig.getRules().get(bizType));
        if (u.getQuantity().intValue() >= activityPointConfig.getRules().get(bizType)) {
            return true;
        }
        return false;
    }

    @Override
    public boolean subPoint(String userId, String activityId, Integer subPoint) {
        UserActivityPointRanking u = find(userId, activityId);
        if (Objects.isNull(u)) {
            return false;
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(u.getId()));
        Update update = new Update();
        update.inc("usedQuantity", subPoint);
        update.set("updatedTime", new Date());
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, UserActivityPointRanking.class);
        return updateResult.getModifiedCount() > 0 ? true : false;
    }

    /**
     * 获取用户积分总榜总榜
     *
     * @param req
     * @param userPO
     * @return
     */
    private RankingListResVO getUserPointRankList(RankingListQueryVO req, UserPO userPO) {
        RankingListResVO rankingListResVO = new RankingListResVO();
        ActivityInfo activityInfo = activityService.findOne(req.getActivityId());
        try {
            if (Objects.isNull(req.getPageSize())) {
                req.setPageSize(10);
            }
            Query query = new Query();
            if (StringUtils.isNotBlank(req.getActivityId())) {
                query.addCriteria(Criteria.where("activityId").is(req.getActivityId()));
                log.info(":>>> 活动ID:{},任务ID:{}", req.getActivityId(), req.getTaskId());
            }
            // 设置省查询
//            String province = this.setRankListProvince(query, req.getActivityId(), req.getProvince(), req.getUserPO());
//            query.with(new Sort(Sort.Direction.DESC, "quantity"));
            Sort sort=Sort.by(Sort.Direction.DESC, "quantity");
            query.with(sort);
            query.limit(req.getPageSize());
            Long count = mongoTemplate.getCollection(mongoTemplate.getCollectionName(UserActivityPointRanking.class)).countDocuments(query.getQueryObject());
            Integer totalPages = 0;
            if (count.intValue() == 0) {
                totalPages = 0;
            } else {
                if (count.intValue() % req.getPageSize() == 0) {
                    totalPages = count.intValue() / req.getPageSize();
                } else {
                    totalPages = count.intValue() / req.getPageSize() + 1;
                }
            }
            log.info(":>>> 用户积分排行榜数量:{},总页数:{},每页大小:{},当前页:{}", count, totalPages, req.getPageSize(), req.getPage());
            List<UserActivityPointRanking> rankings = null;
            if (totalPages < req.getPage()) {
                rankings = new ArrayList<>();
                log.info(":>>> 排行榜总页数:{} 是最后一页:{}", totalPages, req.getPage());

            } else {
                query.skip((req.getPage() - 1) * req.getPageSize());
                MongoCursor iterable = mongoTemplate.getCollection(mongoTemplate.getCollectionName(UserActivityPointRanking.class)).find(query.getQueryObject()).limit(req.getPageSize()).skip((req.getPage() - 1) * req.getPageSize()).
                        noCursorTimeout(true).sort(query.getSortObject()).batchSize(req.getPageSize()).iterator();
                if (Objects.isNull(rankings)) {
                    rankings = new ArrayList<>();
                }
                Document o = null;
                UserActivityPointRanking r = null;
                while (iterable.hasNext()) {
                    o = (Document) iterable.next();
                    if (Objects.nonNull(o)) {
                        r = new UserActivityPointRanking();
//                        r.setId(o.getObjectId("id").toString());
                        r.setName(o.getString("name"));
                        r.setImageUrl(o.getString("imageUrl"));
                        r.setQuantity(o.getDouble("quantity"));
                        r.setUserId(o.getString("userId"));
                        rankings.add(r);
                    }
                }

            }
            List<RankingList> rankingLists = new ArrayList<>();
            boolean in = false;
            int order = 1;
            int uOrder = 1001;
            int orderNN = (req.getPage() - 1) * req.getPageSize() + 1;
            List<String> userIds = new ArrayList<>();
            for (UserActivityPointRanking d : rankings) {
                if (StringUtils.isNotBlank(d.getUserId())) {
                    userIds.add(d.getUserId());
                }
            }
            HashMap<String, UserPO> userPOHashMap = visitorUserService.getByUserIdsAndPlatform(userIds, req.getPlatform(),req.getAppId());
            for (UserActivityPointRanking d : rankings) {
                if (req.getUserId().equals(d.getUserId())) {
                    in = true;
                    uOrder = order;
                }
                RankingList rankingList = new RankingList();
                rankingList.setImageUrl(d.getImageUrl());
                rankingList.setName(d.getName());
                UserPO userPO1 = userPOHashMap.get(d.getUserId());
                this.setNickNameAndImage(userPO1, rankingList);
                if (Objects.nonNull(d.getQuantity())) {
                    BigDecimal b = new BigDecimal(d.getQuantity());
                    Double q = b.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
                    rankingList.setQuantity(q);
                }
//                rankingList.setRank(activityInfo.getRewardValueUnitDesc());
                rankingList.setOrderNo(orderNN);
                if (Objects.nonNull(d.getQuantity())) {
                    rankingList.setRankingValue(d.getQuantity().intValue() + (activityInfo.getRewardValueAlias() == null ? "贡献值" : activityInfo.getRewardValueAlias()));
                }
                rankingLists.add(rankingList);
                order++;
                orderNN++;
            }
            rankingListResVO.setDatas(rankingLists);
            rankingListResVO.setInRanking(in);
            Long uO = rankBaseService.getSingleRankOrder(this.getRankKey(req.getActivityId()), userPO.getId());
            if (Objects.isNull(uO)) {
                uO = 0L;
            }
            Double q = rankBaseService.getSingleRankScore(this.getRankKey(req.getActivityId()), userPO.getId());
            if (Objects.isNull(q)) {
                q = 0D;
            }
            log.info(":>>> 活动:{},用户:{},名次:{},贡献值:{}", req.getActivityId(), req.getUserId(), uO, q);
            rankingListResVO.setOrderNo(uO.intValue());
            rankingListResVO.setQuantity(q);
            rankingListResVO.setRankValue(q.intValue() + (activityInfo.getRewardValueAlias() == null ? "贡献值" : activityInfo.getRewardValueAlias()));
            this.setRankName(rankingListResVO, userPO);
            this.setDefaultHead(rankingListResVO, req.getUserPO().getAvatarUrl());
            rankingListResVO.setInRanking(in);
            rankingListResVO.setPage(req.getPage());
            rankingListResVO.setPageSize(req.getPageSize());
            rankingListResVO.setTotalPages(count.intValue() / req.getPageSize() + 1);
        } catch (Exception e) {
            log.error(":>>> 获取积分排行榜错误:{}", e);
        }
        return rankingListResVO;
    }

    /**
     * 设置排行榜省查询条件
     *
     * @param query
     * @param province
     */
    private String setRankListProvince(Query query, String activityId, String province, UserPO userPO) {
        String res = province;
        if (StringUtils.isNotBlank(province)) {
            if ("-".equals(province)) {
                Query queryU = new Query().addCriteria(Criteria.where("userId").is(userPO.getId()).and(
                        "activityId").is(activityId));
                UserParticipateActivityInfo up = mongoTemplate.findOne(queryU, UserParticipateActivityInfo.class);
                if (Objects.nonNull(up) && StringUtils.isNotBlank(up.getUniversityId())) {
                    Query query1 = new Query().addCriteria(Criteria.where("id").is(up.getUniversityId()));
                    UniversityInfo universityInfo = mongoTemplate.findOne(query1, UniversityInfo.class);
                    if (Objects.nonNull(universityInfo) && StringUtils.isNotBlank(universityInfo.getProvince())) {
                        log.info(":>>> 省查询条件:{}", universityInfo.getProvince());
                        String provinceStr = universityInfo.getProvince();
//                        if (provinceStr.contains("省")) {
//                            provinceStr = provinceStr.replace("省", "");
//                        } else if (provinceStr.contains("市")) {
//                            provinceStr = provinceStr.replace("市", "");
//                        }
                        query.addCriteria(Criteria.where("province").is(provinceStr));
                        res = provinceStr;
                    }
                } else {
                    if (StringUtils.isNotBlank(userPO.getUniversityId())) {
                        Query query1 = new Query().addCriteria(Criteria.where("id").is(userPO.getUniversityId()));
                        UniversityInfo universityInfo = mongoTemplate.findOne(query1, UniversityInfo.class);
                        if (Objects.nonNull(universityInfo) && StringUtils.isNotBlank(universityInfo.getProvince())) {
                            log.info(":>>> 省查询条件:{}", universityInfo.getProvince());
                            String provinceStr = universityInfo.getProvince();
//                            if (provinceStr.contains("省")) {
//                                provinceStr = provinceStr.replace("省", "");
//                            } else if (provinceStr.contains("市")) {
//                                provinceStr = provinceStr.replace("市", "");
//                            }
                            query.addCriteria(Criteria.where("province").is(provinceStr));
                            res = provinceStr;
                        }
                    } else {
                        if (StringUtils.isNotBlank(userPO.getProvince())) {
                            log.info(":>>> 省查询条件:{}", userPO.getProvince());
                            String provinceStr = userPO.getProvince();
//                            if (provinceStr.contains("省")) {
//                                provinceStr = provinceStr.replace("省", "");
//                            } else if (provinceStr.contains("市")) {
//                                provinceStr = provinceStr.replace("市", "");
//                            }
                            query.addCriteria(Criteria.where("province").is(provinceStr));
                            res = provinceStr;
                        }
                    }
                }

            } else {
                log.info(":>>> 省查询条件:{}", province);
                query.addCriteria(Criteria.where("province").is(province));
                res = province;
            }
        }
        return res;
    }

    private void setNickNameAndImage(UserPO userPO1, RankingList d) {
        if (Objects.isNull(userPO1)) {
            return;
        }
        if (StringUtils.isNotBlank(userPO1.getRankNickName())) {
            d.setName(userPO1.getRankNickName());
        } else if (StringUtils.isNotBlank(userPO1.getNickName())) {
            d.setName(userPO1.getNickName());
        } else {
            if (StringUtils.isNotBlank(userPO1.getName())) {
                if (userPO1.getName().length() >= 2) {
                    String s = userPO1.getName().substring(1);
                    d.setName(s);
                } else {
                    d.setName(userPO1.getName());
                }

            } else {
                d.setName(" ");
            }
        }
        if (StringUtils.isNotBlank(userPO1.getRankAvatarUrl())) {
            d.setImageUrl(userPO1.getRankAvatarUrl());
        } else if (StringUtils.isNotBlank(userPO1.getAvatarUrl())) {
            d.setImageUrl(userPO1.getAvatarUrl());
        } else {
            d.setImageUrl(com.youlu.campus.base.base.Constants.defaultHead);
        }
    }

    private void setDefaultHead(RankingListResVO rankingListResVO, String imageUrl) {


        if (StringUtils.isNotBlank(imageUrl)) {
            rankingListResVO.setImageUrl(imageUrl);
        } else {
            rankingListResVO.setImageUrl(defaultHead);
        }
    }

    /**
     * 设置用户名
     *
     * @param req
     * @param userPo
     */
    private void setRankName(RankingListResVO req, UserPO userPo) {
        try {
            if (Objects.nonNull(userPo)) {
                if (StringUtils.isNotBlank(userPo.getNickName())) {
                    req.setName(userPo.getNickName());
                } else if (StringUtils.isNotBlank(userPo.getName())) {
                    req.setName(userPo.getName());
                }
            }
        } catch (Exception e) {
            log.error(":>>> 设置排行榜名字错误:{}", e);
        }
    }

    private UserActivityPointRanking find(@NotNull String userId, @NotNull String activityId) {
        Query query = new Query().addCriteria(Criteria.where("userId").is(userId).and("activityId").is(activityId));
        UserActivityPointRanking u = mongoTemplate.findOne(query, UserActivityPointRanking.class);
        return u;
    }

    @Override
    public Page<ActivityRankingPointsVO> findByPage(UserActivityPointRankingQueryVo queryVo) {

        log.info("findByPage queryVo:{}", JSONObject.toJSONString(queryVo));
        String activityId = queryVo.getActivityId();
        Query query = new Query();
        query.addCriteria(Criteria.where("activityId").is(activityId));
        if (StringUtils.isNotBlank(queryVo.getUniversityId())) {
            query.addCriteria(Criteria.where("universityId").is(queryVo.getUniversityId()));
        }
        if (StringUtils.isNotBlank(queryVo.getName())) {
            query.addCriteria(Criteria.where("name").regex(queryVo.getName(), "i"));
        }
        if (StringUtils.isNotBlank(queryVo.getUserName())) {
            query.addCriteria(Criteria.where("userName").regex(queryVo.getUserName(), "i"));
        }
        if (StringUtils.isNotBlank(queryVo.getMobile())) {
            query.addCriteria(Criteria.where("mobile").is(queryVo.getMobile()));
        }
        PageRequest pageRequest = PageRequest.of(queryVo.getPage() - 1, queryVo.getPageSize());
        Long count = this.mongoTemplate.count(query, UserActivityPointRanking.class);
        if (count == 0) {
            return PageableExecutionUtils.getPage(new ArrayList<>(), pageRequest, () -> count);
        }
        //计数
        AtomicReference<Integer> rankNo = new AtomicReference<>((queryVo.getPage() - 1) * queryVo.getPageSize() + 1);
        //查询列表
        query.with(Sort.by(Sort.Direction.DESC, "quantity"));
        query.with(PageRequest.of(queryVo.getPage() - 1, queryVo.getPageSize()));
        List<UserActivityPointRanking> listTask = this.mongoTemplate.find(query, UserActivityPointRanking.class);
        List<ActivityRankingPointsVO> listRanking = listTask.stream().map(taskRanking -> {
            Integer rankNumber = rankNo.get();
            ActivityRankingPointsVO pointsVO = new ActivityRankingPointsVO();
            pointsVO.setUserId(taskRanking.getUserId());
            pointsVO.setUniversityId(taskRanking.getUniversityId());
            pointsVO.setProvince(taskRanking.getProvince());
            pointsVO.setRankNo(rankNumber);
            pointsVO.setAvatarUrl(taskRanking.getImageUrl());
            pointsVO.setNickName(taskRanking.getName());
            pointsVO.setName(taskRanking.getUserName());
            pointsVO.setUniversityName(taskRanking.getUniversityName());
            pointsVO.setMobile(taskRanking.getMobile());
            pointsVO.setPoints(taskRanking.getQuantity());
            pointsVO.setTenantId(taskRanking.getTenantId());
            pointsVO.setActivityId(taskRanking.getActivityId());
            rankNo.getAndSet(rankNumber + 1);
            return pointsVO;
        }).collect(Collectors.toList());
        return PageableExecutionUtils.getPage(listRanking, pageRequest, () -> count);
    }
}
